package com.gitee.huanminabc.jcommon.file;

import com.gitee.huanminabc.jcommon.base.PatternUtil;
import com.gitee.huanminabc.jcommon.exception.CommonException;
import com.gitee.huanminabc.jcommon.str.StringUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: huanmin
 * @create: 2025-04-03 11:48
 **/
@Slf4j
public class FileUtil {

    public static boolean isFile(String path) {
        File file = new File(path);
        if (!file.exists()) {
            return false;
        }
        return file.isFile();
    }
    //判断是否是文件夹
    public static boolean isDir(String path) {
        File file = new File(path);
        if (!file.exists()) {
            return false;
        }
        return file.isDirectory();
    }


    /**
     * 删除指定目录下的全部文件, 包括子目录里的文件
     *
     * @param derPath 文件夹路径
     * @param pd      true 根目录也删除 ,false不删除根目录
     */
    public static boolean delFilesAll(String derPath, boolean pd) {
        File delfile = new File(derPath);
        if (!delfile.exists()) {
            return false;
        }
        try {
            if (delfile.isFile()) {
                delfile.delete();
                return true;
            } else {
                File[] files = delfile.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (files[i].isDirectory()) {
                        delFilesAll(files[i].getPath(), pd);
                    }
                    files[i].delete();
                }
            }

        } catch (Exception e) {
            log.error("",e);
            return false;
        }
        //根文件夹也 一起删除
        if (pd) {
            delfile.delete();
        }
        return true;
    }



    /**
     * 获取文件夹下的所有文件的数量
     * @param dirPath 文件夹路径
     * @return
     */
    public static int findDirByFileCount(String dirPath) {
        File file = new File(dirPath);
        if (!file.exists()) {
            throw new CommonException("文件路径不存在");
        }
        if (!file.isDirectory()) {
            throw new CommonException("这不是文件夹");
        }
        List<String> filesAll = getFilesAll(dirPath);
        return filesAll.size();
    }





    /**
     * @param dirPath 目录路径
     * @return 返回文件列表
     * 获取指定路径下所有文件, 包括子目录
     */
    public static List<String> getFilesAll(String dirPath) {
        List<String> list = new ArrayList<>();
        getFiles(dirPath, list);
        return list;
    }
    //搜索指定目录下所有正则表达式匹配的文件
    public static List<String> getFilesAll(String dirPath,String regex) {
        List<String> list = new ArrayList<>();
        List<String> filesAll = getFilesAll(dirPath);
        //正则表达式匹配
        for (String filePath : filesAll) {
            if(PatternUtil.isPatternAll(filePath,regex)){
                list.add(filePath);
            }
        }
        return list;
    }
    //找指定目录下的指定文件
    public static List<String> findDirByFileName(String dirPath,String fileName) {
        List<String> list = new ArrayList<>();
        List<String> filesAll = getFilesAll(dirPath);
        //正则表达式匹配
        for (String path : filesAll) {
            File   file =new File(path);
            if(file.getName().equals(fileName)){
                list.add(path);
            }
        }
        return list;
    }

    private static void getFiles(String dirPath, List<String> list) {
        File file = new File(dirPath);
        if (!file.exists()) {
            return;
        }
        //获取当前目录文件集合
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {//判断是否是目录
                getFiles(files[i].getPath(), list);
            } else {
                list.add(files[i].getAbsolutePath());
            }

        }
    }

    /**
     * @param dirPath 目录路径
     * @return 返回文件路径列表
     * 获取指定目录下全部文件, 不包括子目录
     */
    public static List<String> getFiles(String dirPath) {
        List<String> list = new ArrayList<>();
        File file = new File(dirPath);
        if (!file.exists()) {
            return list;
        }
        //获取当前目录文件集合
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {//判断是否是目录
            } else {
                list.add(files[i].getAbsolutePath());
            }
        }
        return list;
    }


    /**
     * 获取文件的路径信息
     * @return
     */
    @SneakyThrows
    public static String getFilePathPartInfo(String pathFile, FilePathPartEnum type) {
        String result;
        File str1 = new File(pathFile);
        String absolutePath = str1.getAbsolutePath();
        int i = absolutePath.lastIndexOf(File.separator) + 1;
        switch (type) {
            case suffix:
                int i2 = str1.getPath().lastIndexOf(".");
                if (i2 == -1) {
                    return "";
                }
                //获取后缀 不加点 比如 txt   js    png
                result = str1.getPath().substring(str1.getPath().lastIndexOf(".") + 1);
                break;
            case suffixAndSpot:
                int i3 = str1.getPath().lastIndexOf(".");
                if (i3 == -1) {
                    return "";
                }
                //获取后缀 加点 比如 .txt   .js    .png
                result = str1.getPath().substring(i3);
                break;
            case prefixName:
                //提取文件前缀  ,比如c:\\xx\xx\helloween.text     结果: helloween
                String substring = absolutePath.substring(i);
                int i1 = substring.lastIndexOf(".");
                if (i1 == -1) {
                    result = substring;
                } else {
                    result = substring.substring(0, i1);
                }
                break;
            case currentDirectory:  //prefixPath
                //提取文件的当前所在的目录的路径 ,不包括文件名称   比如c:\\xx\xx\helloween.text  ,结果:  c:\\xx\xx\
                result = absolutePath.substring(0, i);
                break;
            case parentDirectory:
                result = str1.getParent();
                break;
            case parentDirectoryName:
                int i4 = str1.getParent().lastIndexOf(File.separator);
                if (i4 == -1) {
                    return "";
                }
                //提取当前文件的父级目录,的名称,不含路径   比如c:\\aaa\bbb\helloween.text  结果bbb
                result = str1.getParent().substring(i4 + 1);
                break;
            case prefixPathAndName:
                //提取文件的当前所在的目录的路径 ,包括文件名称,不包括后缀   比如c:\\xx\xx\helloween.text  ,结果:  c:\\xx\xx\helloween
                String substring1 = absolutePath.substring(i);
                int i5 = substring1.lastIndexOf(".");
                if (i5 == -1) {
                    result = substring1;
                } else {
                    substring1.substring(0, i5);
                    String substring2 = absolutePath.substring(0, i);
                    result = substring2 + substring1;
                }
                break;
            default:
                throw new Exception("类型错误,请输入正确的类型");
        }

        return result;
    }

    public static void copyFile(String oldPath, String newPath) {
        copyOrMoveFile(oldPath, newPath, false);
    }
    public static void moveFile(String oldPath, String newPath) {
        copyOrMoveFile(oldPath, newPath, true);
    }
    /**
     * 复制文件
     * @param oldPath 旧文件         src\main\test\a.txt
     * @param newPath 新文件地址     src\main\test\bv\a.txt
     * @param pd      true 移动文件  false 复制文件
     */
    private static void copyOrMoveFile(String oldPath, String newPath, boolean pd) {
        File file = new File(oldPath);
        if (!file.exists()) {
            System.out.println("文件路径不存在");
            return;
        }
        if (file.isFile()) {
            try (
                    FileInputStream fis1 = new FileInputStream(oldPath);
                    BufferedInputStream fis = new BufferedInputStream(fis1);
                    FileOutputStream fos1 = new FileOutputStream(newPath);
                    BufferedOutputStream fos = new BufferedOutputStream(fos1);
            ) {
                byte[] data = new byte[4096];
                int num;
                while ((num = fis.read(data)) != -1) {
                    fos.write(data, 0, num);//写入数据
                }
            } catch (IOException e) {
                throw new CommonException(e);
            }
        } else {
            System.out.println("这不是文件类型 ");
        }
        //删除文件
        if (pd) {
            file.delete();
        }
    }


    /**
     * 重命名文件, 支持文件夹 和文件
     *  C:\xxx\xx\a.txt         C:\xxx\xx\a1.txt
     *  C:\xxx\xx\aaa\xx        C:\xxx\xx\aaa\xx1
     * @param oldPath  文件路径   C:\xxx\xx\aaa
     * @param fileName 重命名的文件名称    xx1
     */
    public static void rename(String oldPath, String fileName) {
        //想命名的原文件的路径
        File file = new File(oldPath);
        //获取源文件路径 不带文件名
        String rootPath = file.getParent();
        //拼接成新的路径(重命名)
        String fileName1 = rootPath + File.separator + fileName;
        //将原文件更改为  fileName
        file.renameTo(new File(fileName1));
    }


    /**
     * 移动文件夹或者复制文件夹
     *
     * @param oldDerPath 原目录地址
     * @param newPath    新目录地址
     * @param pd         true 移动目录   false 复制目录
     * @param exclude    排除的文件或者文件夹,必须全路径
     */

    public static void copyOrMoveFolder(String oldDerPath, String newPath, boolean pd,List<String> exclude) {
        // 复制原目录
        copyFolder(oldDerPath, newPath,exclude);
        // 则删除源目录文件
        if (pd) {
            delFilesAll(oldDerPath, pd);
        }
    }
    // 复制某个目录及目录下的所有子目录和文件到新文件夹
    private static void copyFolder(String oldPath, String newPath,List<String> exclude) {
        if(exclude==null){
            exclude=new ArrayList<>();
        }
        try {
            // 如果文件夹不存在，则建立新文件夹
            File f = new File(newPath);
            if (!f.exists()) {
                f.mkdirs();
            }
            // 读取整个文件夹的内容到file字符串数组，下面设置一个游标i，不停地向下移开始读这个数组
            File filelist = new File(oldPath);
            String[] file = filelist.list();
            // 要注意，这个temp仅仅是一个临时文件指针
            // 整个程序并没有创建临时文件
            File temp = null;
            for (int i = 0; i < file.length; i++) {
                //排除文件或文件夹
                if (excludeFileRule(oldPath + File.separator +file[i],exclude)) {
                    continue;
                }

                // 如果oldPath以路径分隔符/或者\结尾，那么则oldPath/文件名就可以了
                // 否则要自己oldPath后面补个路径分隔符再加文件名
                // 谁知道你传递过来的参数是f:/a还是f:/a/啊？
                if (oldPath.endsWith(File.separator)) {
                    temp = new File(oldPath + file[i]);
                } else {
                    temp = new File(oldPath + File.separator + file[i]);
                }
                // 如果遇到文件
                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    // 复制并且改名
                    FileOutputStream output = new FileOutputStream(newPath
                            + File.separator + (temp.getName()));
                    byte[] bufferarray = new byte[1024 * 64];
                    int prereadlength;
                    while ((prereadlength = input.read(bufferarray)) != -1) {
                        output.write(bufferarray, 0, prereadlength);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                // 如果遇到文件夹
                if (temp.isDirectory()) {
                    copyFolder(oldPath + File.separator + file[i], newPath + File.separator + file[i],exclude);
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println("复制整个文件夹内容操作出错");
            new File(newPath).delete();
        }
    }

    /**
     * 排除文件规则
     * @param file 需要排除的文件
     * @param exclude 排除规则(需要写当前系统中的路径符号) ,支持一个路径中间有多个排除规则,比如: F:\a\**\b\**\c\**\d.txt
     *                目前支持的规则有:
     *                1. 以*开头的,比如/xxx/*.txt,表示排除所有的txt文件
     *                2. 以*结尾的,比如/xxx/static*,表示排除所有的static文件名称开头的文件
     *                3. 以*开头和结尾的,比如/xxx/*test*,表示排除所有包含test的文件名称的文件
     *                4. 以/**结尾的,比如/static/**,表示排除所有的static文件夹下的文件,包括子文件夹
     *                5. 以/*结尾的,比如/static/*,表示排除所有的static文件夹下的文件夹,不包括子文件夹
     *                6. 路径中间有a\\**\\b,表示排除所有的a和b之间的多层文件夹
     *                7. 路径中间有a\\*\\b,表示排除所有的a和b之间的一层文件夹
     * @return
     */
    public static boolean excludeFileRule(String file,List<String> exclude){
        boolean result = false;
        for (String excludeFileRule : exclude) {
            //返回一轮排除后的规则
            String fileResult = excludeFileRuleHandle(file, excludeFileRule);
            if(fileResult==null){
                continue;
            }
            //直到规则都排除完毕
            if(file.equals(fileResult)){
                result = true;
                break;
            }
        }
        return result;
    }

    private static  String excludeFileRuleHandle(String file,String excludeFileRule){

        //如果没有*号,那么就返回
        if(!excludeFileRule.contains("*")){
            return excludeFileRule;//返回处理后的规则
        }

        //判断是结尾是/**, 将/**转换为实际的路径
        if(excludeFileRule.endsWith(File.separator+"**")){
            //去掉/**
            excludeFileRule=excludeFileRule.substring(0,excludeFileRule.length()-3);
            //截取file的后面的字符串
            String substring = file.substring(excludeFileRule.length());
            return excludeFileRuleHandle(file,excludeFileRule+substring);
        }

        //判断结尾是否有/*,将/*转换为实际的路径, 只是第一层
        if(excludeFileRule.endsWith(File.separator+"*")){
            //去掉/*
            excludeFileRule=excludeFileRule.substring(0,excludeFileRule.length()-2);
            //截取file的后面的字符串
            String substring = file.substring(excludeFileRule.length());
            //去掉开头的/
            String newSubstring=substring.substring(1);
            //截取newSubstring第一个文件夹
            newSubstring=newSubstring.substring(newSubstring.indexOf(File.separator)+1);
            return excludeFileRuleHandle(file,excludeFileRule+newSubstring);
        }

        //截取excludeFileRule文件名包括后缀
        String fileRuleName = excludeFileRule.substring(excludeFileRule.lastIndexOf(File.separator) + 1);

        //判断开头和结尾是否都有*,比如: *name*
        if(fileRuleName.startsWith("*")&&fileRuleName.endsWith("*")){
            //取消substring开头的*和结尾的*
            String newFileRuleName=fileRuleName.substring(1,fileRuleName.length()-1);
            //截取file的文件名,包括后缀
            String substring1 = file.substring(file.lastIndexOf(File.separator) + 1);
            //判断file文件名是否包含fileRuleName
            if(substring1.contains(newFileRuleName)){
                //去掉excludeFileRule文件名,包括后缀
                excludeFileRule=excludeFileRule.substring(0,excludeFileRule.lastIndexOf(File.separator) + 1);
                return excludeFileRuleHandle(file,excludeFileRule+substring1);
            }
            return null;
        }

        //判断结尾是否是*,并且前面不能是/, ,比如 /name*, 如果是转换为实际的路径
        if(excludeFileRule.endsWith("*")&&!excludeFileRule.endsWith(File.separator+"*")){
            excludeFileRule=excludeFileRule.substring(0,excludeFileRule.length()-1);//去掉*
            //截取file的文件名,包括后缀
            String substring = file.substring(file.lastIndexOf(File.separator)+1);
            //将excludeFileRule文件名去掉换为file文件名
            excludeFileRule=excludeFileRule.substring(0,excludeFileRule.lastIndexOf(File.separator)+1);
            return excludeFileRuleHandle(file, excludeFileRule+substring);
        }


        //判断开头是否有*  比如: /*name
        if(fileRuleName.startsWith("*")){
            //取消substring开头的*
            fileRuleName=fileRuleName.substring(1);
            //截取file的文件名,包括后缀
            String substring1 = file.substring(file.lastIndexOf(File.separator) + 1);
            //判断file文件名是否是以substring结尾的
            if(substring1.endsWith(fileRuleName)){
                //去掉excludeFileRule文件名,包括后缀
                excludeFileRule=excludeFileRule.substring(0,excludeFileRule.lastIndexOf(File.separator) + 1);
                return excludeFileRuleHandle(file,excludeFileRule+substring1);
            }
            return null;
        }




        //判断中间是否有/**/的,如果有那么就去掉/**/,然后截取/**/前面的字符串,然后在截取/**/后面的字符串,然后将中间补齐
        if(excludeFileRule.contains(File.separator+"**"+File.separator)){
            //获取/**/下标
            int i = excludeFileRule.indexOf(File.separator + "**"+File.separator);
            //获取/**/前面的字符串
            String startPath = excludeFileRule.substring(0, i);
            //获取/**/后面的字符串
            String endPath = excludeFileRule.substring(i + 4);
            //将中间补齐,判断开头和结尾是否都符合,然后将中间的取出来补齐
            if(file.startsWith(startPath)){
                String substring = file.substring(startPath.length());
                if (substring.endsWith(endPath)){
                    return excludeFileRuleHandle(file,startPath+substring);
                }
            }
            return null;
        }

        //判断中间是否有/*/的,如果有那么就去掉/*/,然后截取/*/前面的字符串,然后判断file以startPath开头的,然后在截取/*/后面的字符串,然后判断file文件名是否是以endPath这个字符串结尾的
        if(excludeFileRule.contains(File.separator+"*"+File.separator)){
            //获取/*/下标
            int i = excludeFileRule.indexOf(File.separator + "*"+File.separator);
            //获取/*/前面的字符串
            String startPath = excludeFileRule.substring(0, i);
            //获取/*/后面的字符串
            String endPath = excludeFileRule.substring(i + 3);
            if(file.startsWith(startPath)){
                String substring = file.substring(startPath.length());
                //去掉开头的/
                String newSubstring=substring.substring(1);
                //去掉file开头一级目录
                newSubstring=newSubstring.substring(newSubstring.indexOf(File.separator)+1);
                if (newSubstring.equals(endPath)){
                    return excludeFileRuleHandle(file, startPath+substring);
                }
            }
            return null;
        }




        return null; //这个文件没有任何规则匹配
    }




    /**
     * 计算 当前时间和 文件创建时间 差
     * @param file 传入的文件
     * @param timeUnit   时间单位
     * @return 返回时间差 毫秒
     */
    public long currentTimeDiffer(File file, TimeUnit timeUnit) {
        long diff = System.currentTimeMillis() - new Date(file.lastModified()).getTime();
        switch (timeUnit) {
            case MILLISECONDS:
                // 获得两个时间的毫秒时间差异
                return diff;
            case DAYS:
                // 将时间差转换为天数
                return  TimeUnit.MILLISECONDS.toDays(diff) ;
            default:
                return diff;
        }
    }


    private static final MimetypesFileTypeMap mimeFileTypeMap = new MimetypesFileTypeMap();

    /**
     * 根据文件名称获取  MIME Type   (文件的对应浏览器的类型)
     * Params:
     * fileName – 文件名称
     * Returns:
     */
    public static String getMimeType(String fileName) {
        if (StringUtil.isNotEmpty(fileName) && fileName.indexOf('.') > 0) {
            String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
            suffix = suffix.toLowerCase();
            if ("pdf".equals(suffix) || "ofd".equals(suffix)) {
                return "application/" + suffix;
            } else {
                File file = new File(fileName);
                return mimeFileTypeMap.getContentType(file);
            }
        }
        return "";
    }


    //判断文件是否是根目录的文件  windows系统下的文件 D:\xxx.txt  linux系统下的文件 /xxx.txt
    public static boolean isFilePathRoot(File file) {
        if (file.isFile()) {
            int i = StringUtil.countStr(file.getAbsolutePath(), File.separator);
            return i == 1;
        }
        return false;
    }
}
